////////////////////////////////////////////////
// DESCRIPTION:
//    C++ Wrapper for a DLL module handle
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//
#pragma once

namespace FusionWare
{
    namespace Modules
    {
        // Class for wrapping a DLL Module handle.
        // Automatically unloads the DLL when containing
        // scope is exited.
        //
        class CDllModule  
        {
        public:
            
            /////////////////////////////////////////////
            // {internal}
            // Description:
            //    Entry point information
            //
            //Remarks:
            //    Each entry point in the map describes a single
            //    exported function entry point in the dll.
            //    Required indicates if the function MUST be
            //    exported by the DLL. GetAddresses will fail
            //    if a required entry point is missing. 
            //    Stub is used for optional entry points that
            //    can have a local stub with default behaviour.
            //    Stub may be NULL on optional entry points that
            //    don't require a default stub.
            //
            // See Also:
            //    CDllModule, BEGIN_PROC_MAP(), END_PROCMAP, PROC_MAP_ENTRY_REQUIRED(), PROC_MAP_ENTRY_STUB(), PROC_MAP_ENTRY_OPTIONAL()  
            //
            struct ImportFunctionEntry
            {
                bool Required;          // indicates if the entry is required
                FARPROC Stub;           // Pointer to local stub if any
                const TCHAR* pFuncName; // name of the exported function
                DWORD Offset;           // offset from basepointer to place the
                                        // function pointer for this exported function
            };

        public:
            CDllModule();
            virtual ~CDllModule();
            
            ///////////////////////////////////////////////////////
            // Description: 
            //    Loads the DLL
            //
            // Input: 
            //    Path     - Path of the DLL
            //    Driver   - [Default: false] Flag to indicate if
            //               LoadDriver() should be used to load the DLL
            //    ZoneFlag - Flag to pass to ERRORMSG for displaying messages.
            //
            // Returns: 
            //     BOOL 
            //     
            // Remarks: 
            //    Loads the library or driver DLL using standard 
            //    OS file path search rules.
            //
            // Implementation Notes:
            //    While it might be desirable to call this method
            //    "LoadLibrary" it won't work in situations where
            //    WINCEMACRO is set.
            //
            bool Load(LPCTSTR Path, BOOL ZoneFlag, bool Driver=false);

            ///////////////////////////////////////////////////////
            // Description: 
            //    Gets the address of a procedure in the associated
            //    DLL module
            //
            // Input: 
            //    TCHAR* - name ocf the procedure to get the
            //             address of
            //
            // Returns: 
            //    FARPROC 
            //     
            // Remarks: 
            //    Simple wrapper around ::GetProcAddress()
            //
            FARPROC GetAddress(const TCHAR*);

            operator HMODULE();
            operator bool();
            bool Unload();
        
        protected:
            HMODULE hModule;

            ///////////////////////////////////////////////////////
            // Description: 
            //    Retrieve a set of addresses from the module
            //
            // Input: 
            //    pBase    - base from which the entries contain offsets
            //    ZoneFlag - DebugZone flag to use for showing Error messages.
            //
            // Returns: 
            //     BOOL 
            //     
            // Remarks: 
            //   This method is used by derived classes to retrieve
            //   all of the proc addresses at once. GetFunctionEntries()
            //   provides a table of ImportFunctionEntry entries that
            //   indicate the name of the function, the offset from pBase,
            //   and a flag indicating if the function is required (useful
            //   for layering device drivers)
            //
            bool GetProcAddresses(void* pBase, BOOL ZoneFlag);

        protected:
            virtual const ImportFunctionEntry* GetFunctionEntries() { return NULL; }
        };

        inline CDllModule::operator HMODULE()
        {
            return this->hModule;
        }
        
        inline CDllModule::operator bool()
        {
            return this->hModule!=NULL;
        }

        inline FARPROC CDllModule::GetAddress(LPCTSTR ProcName)
        {
            return GetProcAddress(this->hModule, ProcName);
        }

///////////////////////////////////////////////////////////////
//Description:
//    Begins a procedure map for a Dll Module.
//
// Input:
//    x - name of the class containing the map
//
// Remarks:
//    Each entry in the Procedure map identifies an
//    exported function from the DLL. GetAddresses()
//    Uses this table to get the address of all the
//    functions exported by the DLL and will fail if
//    any required functions are missing. This simplifies
//    the process of getting addresses and verifying
//    the required entries are present by making it
//    declaritive.
//    
//    For each entry in the map there *MUST* be a
//    corresponding pointer to function data member to
//    receive the location of the Dlls function. 
//
// Example:
//        class MyDll : public CDllModule
//        {
//            ///////////////////////////////////////////////////////
//            // Description: 
//            //    Loads the DLL
//            //
//            // Input: 
//            //    Path - Path of the DLL
//            //
//            // Remarks: 
//            //    Loads the DLL using standard 
//            //    OS file path search rules and retrieves
//            //    all the entry point addresses. 
//            //
//            bool Load(const TCHAR* Path, BOOL ZoneFlag)
//            { 
//                if(!CDllModule::Load(Path, ZoneFlag, true))
//                    return false;
//                
//                return GetProcAddresses(this, ZoneFlag);
//            }
//
//            BEGIN_PROC_MAP(MyDll)
//                PROC_MAP_ENTRY_REQUIRED(Foo)
//                PROC_MAP_ENTRY_STUB(Bar)
//                PROC_MAP_ENTRY_OPTIONAL(Baz)
//            END_PROCMAP
//
//        public:
//            BOOL (*Foo)(int arg);
//            BOOL (*Bar)(int arg);
//            void (*Baz)(int arg);
//
//            static BOOL BarStub(int arg)
//            {
//                SetLastError(ERROR_NOT_SUPPORTED);
//                return FALSE;
//            }
//
//            // simple wrapper to test for NULL pointer
//            // before actually calling through the pointer.
//            // This keeps things isolated and consistent
//            // and prevents callers from needing to 
//            // remember which function pointers they need
//            // to test for NULL before calling through them.
//            //
//            inline void Baz(int arg)
//            {
//                if(this->pBaz!=Null)
//                    this->pBaz(arg);
//            }
//        };
//
//        [Calling code]
//        {
//           MyDll theDLL;
//           if(!theDLL.Load("\\windows\\MyDll.Dll", ZONE_ERROR))
//           { /* Handle error */ }
//
//           if(!theDLL.Foo(arg))
//           { /* Handle error */ }
//
//           ...
//
//           if(!theDLL.Bar(arg))
//           { /* Handle error */ }
//
//           ...
//
//           theDLL.Baz(arg);
//
//           // MyDll.Dll is automatically unloaded here - at scope closure
//       }
// 
// See Also:
//    CDllModule, END_PROCMAP, PROC_MAP_ENTRY_REQUIRED(), PROC_MAP_ENTRY_STUB(), PROC_MAP_ENTRY_OPTIONAL()  
//
#define BEGIN_PROC_MAP(x)\
        typedef x __ModuleType;\
        virtual const ImportFunctionEntry* GetFunctionEntries() {\
        static const ImportFunctionEntry pEntries[] = {

///////////////////////////////////////////////////////////////
//Description:
//   Defines a required Entry point
//
// Input:
//   ProcName - Name of the entry point in the DLL
//
//Remarks:
//   The pointer to the function in the DLL is assigned to a
//   member function pointer whose name should match ProcName.
//   The return value and signature of the function pointer
//   should match the actual function usage as appropriate.
// 
// Example:
//        class MyDll : public CDllModule
//        {
//            ///////////////////////////////////////////////////////
//            // Description: 
//            //    Loads the DLL
//            //
//            // Input: 
//            //    Path - Path of the DLL
//            //
//            // Remarks: 
//            //    Loads the DLL using standard 
//            //    OS file path search rules and retrieves
//            //    all the entry point addresses. 
//            //
//            bool Load(const TCHAR* Path, BOOL ZoneFlag)
//            { 
//                if(!CDllModule::Load(Path, ZoneFlag, true))
//                    return false;
//                
//                return GetProcAddresses(this, ZoneFlag);
//            }
//
//            BEGIN_PROC_MAP(MyDll)
//                ...
//                PROC_MAP_ENTRY_REQUIRED(Foo)
//                ...
//            END_PROCMAP
//
//        public:
//            BOOL (*Foo)(int arg);
//        };
//
//        [Calling code]
//        {
//           MyDll theDLL;
//
//           if(!theDLL.Load("\\windows\\MyDll.Dll", ZONE_ERROR))
//               return FALSE;
//
//           if(!theDLL.Foo(arg))
//           { /* Handle error */ }
//
//           // MyDll.Dll is automatically unloaded here - at scope closure
//       }
//
// See Also:
//    CDllModule, BEGIN_PROC_MAP, END_PROCMAP, PROC_MAP_ENTRY_STUB(), PROC_MAP_ENTRY_OPTIONAL()  
//
#define PROC_MAP_ENTRY_REQUIRED(ProcName)\
        { true, NULL, _T(#ProcName), offsetof(__ModuleType, ProcName) },

///////////////////////////////////////////////////////////////
//Description:
//   Defines an optional Entry point that has a stub.
//
// Input:
//   ProcName - Name of the entry point in the DLL
//
//Remarks:
//   The stub should be a static method of the class and should
//   match ProcName##Stub. 
//
// Example:
//         PROC_MAP_ENTRY_STUB(PowerUp)
//         ...
//         static void PowerUpStub(DWORD hDeviceContext)
//         {
//         }
//
// See Also:
//    CDllModule, BEGIN_PROC_MAP, END_PROCMAP, PROC_MAP_ENTRY_REQUIRED(), PROC_MAP_ENTRY_OPTIONAL()  
//
#define PROC_MAP_ENTRY_STUB(ProcName)\
        { false, (FARPROC)ProcName##Stub, _T(#ProcName), offsetof(__ModuleType, ProcName) },


///////////////////////////////////////////////////////////////
//Description:
//   Defines an optional Entry point that does not need
//   a stub. 
//
// Remarks:
//   Users of the Dll module MUST check the member pointer
//   that matches p##ProcName. You should also create an 
//   inline method that checks the pointer for NULL before
//   calling it. 
//
// Example:
//    BEGIN_PROC_MAP(MyDll)
//        ...
//        PROC_MAP_ENTRY_OPTIONAL(Baz)
//    END_PROCMAP
//
//    // simple wrapper to test for NULL pointer
//    // before actually calling through the pointer.
//    // This keeps things isolated and consistent
//    // and prevents callers from needing to 
//    // remember which function pointers they need
//    // to test for NULL before calling through them.
//    //
//    inline void MyDll::Baz(int arg)
//    {
//       if(this->pBaz!=Null)
//          this->pBaz(arg);
//    }
//
//    [Calling code]
//    {
//       ...
//       myDllinstance.Baz(arg);
//       ...
//    }
//
// See Also:
//    CDllModule, BEGIN_PROC_MAP, END_PROCMAP, PROC_MAP_ENTRY_REQUIRED(), PROC_MAP_ENTRY_STUB()  
//
#define PROC_MAP_ENTRY_OPTIONAL(ProcName)\
        { false, NULL, _T(#ProcName), offsetof(__ModuleType, ProcName) },

///////////////////////////////////////////////////////////////
//Description:
//   Marks the end of a Dll module proc entry point map.
//
#define END_PROC_MAP\
        { FALSE, NULL, 0} }; return pEntries; }

        ///////////////////////////////////////////////////////
        // Description: 
        //   Simple extension of CDllModule to support loading 
        //   a Driver DLL.
        //
        // Remarks:
        //   The OS does not provide any standard mechanisms for
        //   layering or filtering on top of an existing device
        //   driver. This class is useful in situations where 
        //   that filtering is desired. You can simply replace
        //   the driver's "Dll" entry in the registry with your
        //   own "filter" that implements the same exported APIs
        //   as the real driver and then uses this class to load
        //   the original driver. 
        //
        // NOTE:
        //   This implementation assumes naked entries for the
        //   imported names. If a prefix is needed then you
        //   will need derive from this module and implement handling
        //   for that. 
        //
        class CDriverDll : public CDllModule
        {
        public:        
            ///////////////////////////////////////////////////////
            // Description: 
            //    Loads the Driver DLL
            //
            // Input: 
            //    Path - Path of the Driver DLL
            //
            // Returns: 
            //     BOOL 
            //     
            // Remarks: 
            //    Loads the driver DLL using standard 
            //    OS file path search rules.
            //
            bool Load(const TCHAR* Path, BOOL ZoneFlag)
            { 
                if(!CDllModule::Load(Path, ZoneFlag, true))
                    return false;
                
                return GetProcAddresses(this, ZoneFlag);
            }

        public:
            DWORD (*Init)(DWORD dwContext);
            BOOL (*PreDeinit)(DWORD hDeviceContext);
            BOOL (*Deinit)(DWORD hDeviceContext);
            DWORD(*Open)(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode);
            BOOL (*PreClose)(DWORD hOpenContext);
            BOOL (*Close)(DWORD hOpenContext);
            BOOL (*IOControl)(DWORD hOpenContext, DWORD IoCode,BYTE* pInBuf, DWORD InBufSize,BYTE* pOutBuf, DWORD OutBufSize,DWORD* pActualOut);
            DWORD (*Read)(DWORD hOpenContext,void* pBuffer,DWORD Count);
            DWORD (*Write)(DWORD hOpenContext,const void* pSourceBytes,DWORD NumberOfBytes);
            DWORD (*Seek)(DWORD hOpenContext, long Amount,DWORD Type);
            void (*PowerUp)(DWORD hDeviceContext);
            void (*PowerDown)(DWORD hDeviceContext);

        public:
            BEGIN_PROC_MAP(CDriverDll)
                PROC_MAP_ENTRY_REQUIRED(Init)
                PROC_MAP_ENTRY_STUB(PreDeinit)
                PROC_MAP_ENTRY_STUB(Deinit)
                PROC_MAP_ENTRY_STUB(Open)
                PROC_MAP_ENTRY_STUB(PreClose)
                PROC_MAP_ENTRY_STUB(Close)
                PROC_MAP_ENTRY_STUB(IOControl)
                PROC_MAP_ENTRY_STUB(Read)
                PROC_MAP_ENTRY_STUB(Write)
                PROC_MAP_ENTRY_STUB(Seek)
                PROC_MAP_ENTRY_STUB(PowerUp)
                PROC_MAP_ENTRY_STUB(PowerDown)
            END_PROC_MAP

        private:
            // Implementation Notes:
            //    This method is hidden as private so that
            //    a driver is always loaded with LoadDriver()
            BOOL Load(LPCTSTR Path, bool Driver);

        // Group = Stub functions for optional entry points. 
        private:    
            static BOOL PreDeinitStub(DWORD hDeviceContext)
            {
                SetLastError(ERROR_NOT_SUPPORTED);
                return FALSE;
            }

            static BOOL DeinitStub(DWORD hDeviceContext)
            {
                SetLastError(ERROR_NOT_SUPPORTED);
                return FALSE;
            }

            static DWORD OpenStub(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode)
            {
                SetLastError(ERROR_NOT_SUPPORTED);
                return 0;
            }

            static BOOL PreCloseStub(DWORD hOpenContext)
            {
                SetLastError(ERROR_NOT_SUPPORTED);
                return FALSE;
            }

            static BOOL CloseStub(DWORD hOpenContext)
            {
                SetLastError(ERROR_NOT_SUPPORTED);
                return FALSE;
            }

            static BOOL IOControlStub(DWORD hOpenContext, DWORD IoCode,BYTE* pInBuf, DWORD InBufSize,BYTE* pOutBuf, DWORD OutBufSize,DWORD* pActualOut)
            {
                SetLastError(ERROR_NOT_SUPPORTED);
                return FALSE;
            }

            static DWORD ReadStub(DWORD hOpenContext,void* pBuffer,DWORD Count)
            {
                SetLastError(ERROR_NOT_SUPPORTED);
                return 0;
            }

            static DWORD WriteStub(DWORD hOpenContext,const void* pSourceBytes,DWORD NumberOfBytes)
            {
                SetLastError(ERROR_NOT_SUPPORTED);
                return 0;
            }

            static DWORD SeekStub(DWORD hOpenContext, long Amount,DWORD Type)
            {
                SetLastError(ERROR_NOT_SUPPORTED);
                return 0;
            }

            static void PowerUpStub(DWORD hDeviceContext)
            {
            }

            static void PowerDownStub(DWORD hDeviceContext)
            {
            }
        };
    }
}
